/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.derivative;

import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.base.Namespace;
import org.proteusframework.platformservice.persistence.c14n.C14NFactory;
import org.proteusframework.platformservice.persistence.messagebean.DataType;

import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Date;
import java.util.logging.Logger;

/**
 * @author Tacoma Four
 */
@SuppressWarnings({"ConstantConditions"})
final class DataOutputStreamDerivativeConverter implements IDerivativeConverter<DataOutputStream>
{
    private static final Logger logger = Logger.getLogger(DataOutputStreamDerivativeConverter.class.getCanonicalName());

    @Override
    public void convert(DataOutputStream targetDerivative, DataType dataType, Object value, Object... parameters)
    {
        try
        {
            if (null == value)
            {
                targetDerivative.writeBoolean(true);
                logger.fine("NULL FLAG: true");
            } else
            {
                targetDerivative.writeBoolean(false);
                logger.fine("NULL FLAG: false");

                switch (dataType)
                {
                    case BigDecimalType:
                        BigDecimal bd = (BigDecimal) value;
                        targetDerivative.writeUTF(bd.toString());
                        logger.fine("WRITE UTF");
                        break;
                    case BigIntegerType:
                        targetDerivative.writeLong((Long) value);
                        logger.fine("WRITE LONG");
                        break;
                    case BooleanType:
                        targetDerivative.writeBoolean((Boolean) value);
                        logger.fine("WRITE BOOLEAN");
                        break;
                    case ByteArrayType:
                        byte[] bytes = (byte[]) value;
                        targetDerivative.writeInt(bytes.length);
                        logger.fine("WRITE INT (bytes.length = " + bytes.length);
                        targetDerivative.write(bytes);
                        logger.fine("WRITE BYTES");
                        break;
                    case ByteType:
                        logger.fine("WRITE BYTE");
                        targetDerivative.write((Byte) value);
                        break;
                    case CharArrayType:
                        char[] chars = (char[]) value;
                        targetDerivative.writeUTF(String.valueOf(chars));
                        logger.fine("WRITE UTF");
                        break;
                    case CharType:
                        targetDerivative.writeUTF(value.toString());
                        logger.fine("WRITE UTF");
                        break;
                    case DateType:
                        targetDerivative.writeLong(((Date) value).getTime());
                        logger.fine("WRITE LONG");
                        break;
                    case DoubleType:
                        targetDerivative.writeDouble((Double) value);
                        logger.fine("WRITE DOUBLE");
                        break;
                    case EnumType:
                        targetDerivative.writeInt(((Enum) value).ordinal());
                        targetDerivative.writeUTF(value.getClass().getCanonicalName());
                        logger.fine("WRITE INT");
                        break;
                    case FloatType:
                        targetDerivative.writeFloat((Float) value);
                        logger.fine("WRITE FLOAT");
                        break;
                    case IntegerType:
                        targetDerivative.writeInt((Integer) value);
                        logger.fine("WRITE INT");
                        break;
                    case LongType:
                        targetDerivative.writeLong((Long) value);
                        logger.fine("WRITE LONG");
                        break;
                    case NamespaceType:
                        targetDerivative.writeUTF(Namespace.toCanonical((INamespace) value, true));
                        logger.fine("WRITE UTF");
                        break;
                    case ShortType:
                        targetDerivative.writeShort((Short) value);
                        logger.fine("WRITE SHORT");
                        break;
                    case StringType:
                        targetDerivative.writeUTF(value.toString());
                        logger.fine("WRITE UTF");
                        break;
                    case TimeType:
                        logger.fine("WRITE LONG");
                        if (value instanceof java.util.Date)
                        {
                            targetDerivative.writeLong(((Date) value).getTime());
                        } else
                        {
                            targetDerivative.writeLong(((Time) value).getTime());
                        }
                        break;
                    case TimestampType:
                        logger.fine("WRITE LONG");
                        if (value instanceof java.util.Date)
                        {
                            targetDerivative.writeLong(((Date) value).getTime());
                        } else
                        {
                            targetDerivative.writeLong(((Timestamp) value).getTime());
                        }
                        break;
                    case URLType:
                        targetDerivative.writeUTF(value.toString());
                        logger.fine("WRITE UTF");
                        break;
                    case UUIDType:
                        targetDerivative.writeUTF(value.toString());
                        logger.fine("WRITE UTF");
                        break;
                    case XmlType:
                        targetDerivative.writeUTF(value.toString());
                        logger.fine("WRITE UTF");
                        break;
                    default:
                        byte[] arrayBytes = C14NFactory.getHandler(dataType).toC14N(value);
                        logger.fine("WRITE C14N Bytes: " + arrayBytes.length);
                        targetDerivative.writeInt(arrayBytes.length);
                        targetDerivative.write(arrayBytes, 0, arrayBytes.length);
                        targetDerivative.flush();
                        logger.fine("WRITE BYTES");
                }
            }
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
